#pragma once

#include "actor.h"

class TestInteraction
{
	class Target;
	class Damage;
	class Tick;
	class SetTarget;
	class TargetsFactory;
	class CastersFactory;

	class TargetReplica
	{
	public :
		TargetReplica ()
		: health ( 0 )
		{
		}
		int health;
	};
	
	class Target
	: public Actor < Target, TargetReplica >
	, public TargetReplica
	{
		virtual void UpdateReplica ( TargetReplica & replica ) const
		{
			replica = * this;
		}
	public:
		Target ()
		{
			health = 10;
		}
		void TakeDamage ()
		{
			health -= 1;
		}
	};
	
	class CasterReplica
	{
	};
	
	class Caster
	: public Actor < Caster, CasterReplica >
	, public CasterReplica
	{
		virtual void UpdateReplica ( CasterReplica & replica ) const
		{
			replica = * this;
		}
	public:
		Caster ()
		{
		}
		void Tick ()
		{
			if ( target.GetReplica () && target.GetReplica ()->health > 0 )
				Damage::SendTo ( target );
			Tick::SendTo ( GetId () );
		}
		Target::Id target;
	};

	class CastersFactory
	: public ActorsFactory < Caster, CasterReplica >
	{
	public:
		Id Create ()
		{
			return RegisterNewActor ( new Caster () );
		}
	};
	
	class TargetsFactory
	: public ActorsFactory < Target, TargetReplica >
	{
	public:
		Id Create ()
		{
			return RegisterNewActor ( new Target () );
		}
	};
	
	class Damage
	: public Message < Target, TargetReplica >
	{
		virtual void Execute ( Target & target )
		{
			target.TakeDamage ();
		}
	public:
		static void SendTo ( const Target::Id & target )
		{
			SendNewMessage ( target, new Damage () );
		}
	};
	
	class Tick
	: public Message < Caster, CasterReplica >
	{
		virtual void Execute ( Caster & caster )
		{
			caster.Tick ();
		}
	public:
		static void SendTo ( const Caster::Id & caster )
		{
			SendNewMessage ( caster, new Tick () );
		}
	};
	
	class SetTarget
	: public Message < Caster, CasterReplica >
	{
		SetTarget ( const Target::Id & target )
		: target ( target )
		{
		}
		virtual void Execute ( Caster & caster )
		{
			caster.target = target;
		}
	public:
		static void SendTo ( const Caster::Id & caster, const Target::Id & target )
		{
			SendNewMessage ( caster, new SetTarget ( target ) );
		}
	private:
		Target::Id target;
	};
	
public:
	static void Run ()
	{
		CastersFactory castersFactory;
		TargetsFactory targetsFactory;
		Caster::Id casterId = castersFactory.Create ();
		Target::Id targetId = targetsFactory.Create ();
		
		Tick::SendTo ( casterId );
		SetTarget::SendTo ( casterId, targetId );
		assert ( targetId.GetReplica ()->health == 10 );
		for ( int i = 0; i < 100; i++ )
		{
			castersFactory.HandleMessages ();
			targetsFactory.HandleMessages ();
			castersFactory.UpdateReplicas ();
			targetsFactory.UpdateReplicas ();
		}
		assert ( targetId.GetReplica ()->health == 0 );
	}
};
